// Rust需要知道每个函数返回值占用多少内存，因此需要用trait来定义返回值类型。
// 不像其他语言，Rust不允许函数返回一个trait对象。因为不同的对象占用的memory size可能不同
// 因此，Rust要求返回值必须是具体类型，而不是trait对象。
// 但是可以返回一个Box<dyn Trait>，Box<dyn Trait>是一个trait对象，可以指向任何实现了Trait的对象。
// 因为Box是一个指针类型，它的大小是固定的，因此可以返回一个Box<dyn Trait>作为函数的返回值。

// 所以，如果要返回一个trait对象，可以用Box<dyn Trait>来包装它。

struct Sheep {}
struct Cow {}

trait Animal {
    // Instance method signature
    fn noise(&self) -> &'static str;
}

// Implement the `Animal` trait for `Sheep`.
impl Animal for Sheep {
    fn noise(&self) -> &'static str {
        "baaaaah!"
    }
}

// Implement the `Animal` trait for `Cow`.
impl Animal for Cow {
    fn noise(&self) -> &'static str {
        "moooooo!"
    }
}

// Returns some struct that implements Animal, but we don't know which one at compile time.
// 原来在Java中返回Object或接口的，现在用Box<dyn Trait>来包装。
fn random_animal(random_number: f64) -> Box<dyn Animal> {
    if random_number < 0.5 {
        Box::new(Sheep {})
    } else {
        Box::new(Cow {})
    }
}

fn main() {
    let random_number = 0.234;
    let animal = random_animal(random_number);
    println!(
        "You've randomly chosen an animal, and it says {}",
        animal.noise()
    );
}
